// $Id: url.c 88 2011-10-17 18:00:11Z cedric.shih@gmail.com $
/*
 * Copyright 2011 Cedric Shih (cedric dot shih at gmail dot com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "log.h"

#include "url.h"

enum niortsp_url_state {
	HORUS_URL_PROTO,
	HORUS_URL_USER,
	HORUS_URL_PASS,
	HORUS_URL_HOST,
	HORUS_URL_PORT,
	HORUS_URL_PATH
};

static void niortsp_url_reset(struct niortsp_url *url);

struct niortsp_url *niortsp_url_new(void)
{
	struct niortsp_url *url;

	niortsp_debug("constructing niortsp_url");

	if (!(url = calloc(1, sizeof(struct niortsp_url)))) {
		niortsp_error("failed to calloc niortsp_url");
		return NULL;
	}

	return url;
}

void niortsp_url_free(struct niortsp_url *url)
{
	if (!url) return;

	niortsp_debug("destructing niortsp_url");

	niortsp_url_reset(url);

	free(url);
}

void niortsp_url_reset(struct niortsp_url *url)
{
	if (url->string) {
		free(url->string);
	}

	if (url->scheme) {
		free(url->scheme);
	}

	if (url->username) {
		free(url->username);
	}

	if (url->password) {
		free(url->password);
	}

	if (url->host) {
		free(url->host);
	}

	if (url->path) {
		free(url->path);
	}

	memset(url, '\0', sizeof(struct niortsp_url));
}

int niortsp_url_parse(struct niortsp_url *url, const char *str)
{
	int rc, val;
	size_t len;
	char *start, *end, *middle;

	if (!url || !str) {
		return EINVAL;
	}

	niortsp_url_reset(url);

	niortsp_debug("parsing URL from: %s", str);

	len = strlen(str);

	if (!(url->string = malloc(len + 1))) {
		rc = ENOMEM;
		goto exception;
	}

	memcpy(url->string, str, len + 1);
	start = url->string;

	if (!(end = strchr(start, ':'))) {
		goto invalid;
	}

	if (*(end + 1) != '/') {
		goto invalid;
	}

	if (*(end + 2) != '/') {
		goto invalid;
	}

	*end = '\0';

	if (!(url->scheme = strdup(start))) {
		rc = ENOMEM;
		goto exception;
	}

	if (*(start = end + 3) == '\0') {
		goto invalid;
	}

	if ((end = strchr(start, '@'))) {
		*end = '\0';
		if ((middle = strchr(start, ':'))) {
			*middle = '\0';
			if (!(url->password = strdup(middle + 1))) {
				rc = ENOMEM;
				goto exception;
			}
		}

		if (!(url->username = strdup(start))) {
			rc = ENOMEM;
			goto exception;
		}

		start = end + 1;
	}

	if ((end = strchr(start, '/'))) {
		if (!(url->path = strdup(end))) {
			rc = ENOMEM;
			goto exception;
		}

		if (start == end) {
			rc = 0;
			goto finally;
		}

		*end = '\0';
	} else {
		end = strchr(start, '\0');
	}

	if ((middle = strchr(start, ':')) && middle < end) {
		*middle = '\0';
		val = atoi(middle + 1);
		if (val <= 0 || val > 65535)
			goto invalid;
		url->port = val;
	}

	if (!(url->host = strdup(start))) {
		rc = ENOMEM;
		goto exception;
	}

	rc = 0;
	goto finally;

invalid:
	rc = EPROTO;

exception:
	niortsp_url_reset(url);

finally:
	if (rc == 0) {
		memcpy(url->string, str, len + 1);
	}
	return rc;
}
